using System;
using System.Collections.Generic;
using System.Text;

namespace RapidHDL.Fabric
{
    public class ContainerBlock : Block 
    {
        Dictionary<string, Block> dicBlocks = new Dictionary<string, Block>();
        Dictionary<string, ControlState> dicControlStates = new Dictionary<string, ControlState>();

        Dictionary<string, ExternalIOBlock> dicExternalInputs = new Dictionary<string, ExternalIOBlock>();
        Dictionary<string, ExternalIOBlock> dicExternalOutputs = new Dictionary<string, ExternalIOBlock>();

        Dictionary<string,ExternalBusBlock> dicExternalBusInputs = new Dictionary<string, ExternalBusBlock>();
        Dictionary<string,ExternalBusBlock> dicExternalBusOutputs = new Dictionary<string, ExternalBusBlock>();



        Dictionary<string, ContainerBlock> dicContainerBlocks = new Dictionary<string,ContainerBlock>();
        Dictionary<StatusOutput, Output> dicNewStatusOutputs = new Dictionary<StatusOutput, Output>();

        SortedDictionary<string, SignalFormat> dicSignalViews = new SortedDictionary<string, SignalFormat>();

        public ContainerBlock(string psName)
            : base(psName,"CONT")
        {
        }

        public ALUBlock CreateALU(string psName, ALUType peType, int piWidth)
        {
            return CreateALU(psName,peType,piWidth,0);
        }

        public ALUBlock CreateALU(string psName, ALUType peType, int piWidth, int piShiftWidth)
        {
            ALUBlock oBlock = new ALUBlock(psName,peType,piWidth,piShiftWidth);
            dicBlocks.Add(psName, oBlock);
            return oBlock;
        }

        public ContainerBlock CreateContainerBlock(string psName)
        {
            ContainerBlock oBlock = new ContainerBlock(psName);
            dicBlocks.Add(psName,oBlock);
            dicContainerBlocks.Add(psName, oBlock);
            return oBlock;
        }


        public ExternalIOBlock CreateExternalInput(string psName, int piWidth)
        {
            ExternalIOBlock oBlock = new ExternalIOBlock(psName, piWidth);
            dicBlocks.Add(psName, oBlock);
            dicExternalInputs.Add(psName, oBlock);
            oBlock.Inputs["IN"].IgnoreConnectionError = true;
            return oBlock;
        }

        public ExternalIOBlock CreateExternalOutput(string psName, int piWidth)
        {
            ExternalIOBlock oBlock = new ExternalIOBlock(psName, piWidth);
            dicBlocks.Add(psName, oBlock);
            dicExternalOutputs.Add(psName, oBlock);
            return oBlock;
        }

        public ExternalBusBlock CreateExternalBusInput(string psName, int piWidth)
        {
            ExternalBusBlock oBlock = new ExternalBusBlock(psName, piWidth);
            dicBlocks.Add(psName, oBlock);
            dicExternalBusInputs.Add(psName, oBlock);
            oBlock.Inputs["IN"].IgnoreConnectionError = true;
            oBlock.ControlInputs["LATCH"].IgnoreConnectionError = true;
            return oBlock;
        }

        public ExternalBusBlock CreateExternalBusOutput(string psName, int piWidth)
        {
            ExternalBusBlock oBlock = new ExternalBusBlock(psName, piWidth);
            dicBlocks.Add(psName, oBlock);
            oBlock.ControlInputs["READ"].IgnoreConnectionError = true;
            dicExternalBusOutputs.Add(psName, oBlock);
            return oBlock;
        }


        public RAMBlock CreateRAM(string psName, int piWidth, int piDepth, bool pbRisingEdge)
        {
            RAMBlock oBlock = new RAMBlock(psName, piWidth, piDepth, pbRisingEdge);
            dicBlocks.Add(psName, oBlock);
            return oBlock;
        }

        public ROMBlock CreateROM(string psName, int piWidth, int piDepth, bool pbRisingEdge)
        {
            ROMBlock oBlock = new ROMBlock(psName, piWidth, piDepth);
            dicBlocks.Add(psName, oBlock);
            return oBlock;
        }

        public int AddROMValue(string psName, int piValue, bool pbCompact)
        {
            Block oBlock = dicBlocks[psName];
            ROMBlock oROMBlock = (ROMBlock)oBlock;
            return oROMBlock.AddValue(piValue,pbCompact);
        }

        public RegisterBlock CreateRegister(string psName, int piWidth, bool pbRisingEdge)
        {
            RegisterBlock oBlock = new RegisterBlock(psName, piWidth, pbRisingEdge);
            dicBlocks.Add(psName, oBlock);
            return oBlock;
        }

        public MicroCoreTable GetMicroCore(string psName)
        {
            if (dicBlocks.ContainsKey(psName))
            {
                MicroCoreBlock oBlock = (MicroCoreBlock)dicBlocks[psName];
                return oBlock.GetTable();
            }
            return null;
        }

        public MicroCoreBlock CreateMicroCore(string psName, MicroCoreTable poTable)
        {
            MicroCoreBlock oBlock = new MicroCoreBlock(psName, poTable, dicControlStates, dicStatusOutputs);
            dicBlocks.Add(psName, oBlock);
            return oBlock;
        }

        public ProgramROMBlock CreateProgramROM(string psName, MicroCoreTable poTable, string psProgramPath)
        {
            ProgramROMBlock oBlock = new ProgramROMBlock(psName,poTable,psProgramPath);
            dicBlocks.Add(psName, oBlock);
            return oBlock;
        }

        public ProgramROMRAMBlock CreateProgramROMRAM(string psName, MicroCoreTable poTable, string psROMProgramPath, string psRAMProgramPath, int piRAMOffset, int piDepth, int piConstWidth)
        {
            ProgramROMRAMBlock oBlock = new ProgramROMRAMBlock(psName, poTable, psROMProgramPath, psRAMProgramPath, piRAMOffset, piDepth, piConstWidth);
            dicBlocks.Add(psName, oBlock);
            return oBlock;
        }


        public LogicBlock CreateLogicBlock(string psName, string psExpression)
        {
            LogicBlock oBlock = new LogicBlock(psName, psExpression);
            dicBlocks.Add(psName,oBlock);
            return oBlock;
        }

        public ResizeBlock CreateResize(string psName, int piInputWidth, int piOutputWidth)
        {
            ResizeBlock oBlock = new ResizeBlock(psName, piInputWidth, piOutputWidth);
            dicBlocks.Add(psName, oBlock);
            return oBlock;
        }

        public ConstantBlock CreateConstant(string psName, int piValue, int piWidth)
        {
            ConstantBlock oBlock = new ConstantBlock(psName, piValue, piWidth);
            dicBlocks.Add(psName, oBlock);
            return oBlock;
        }

        public CombineBlock CreateCombine(string psName, params int[] piWidths)
        {
            CombineBlock oBlock = new CombineBlock(psName, piWidths);
            dicBlocks.Add(psName, oBlock);
            return oBlock;
        }

        public BreakoutBlock CreateBreakout(string psName, params int[] piWidths)
        {
            BreakoutBlock oBlock = new BreakoutBlock(psName, piWidths);
            dicBlocks.Add(psName, oBlock);
            return oBlock;
        }

        public void Join(string psOutputBlock, string psOutputName, string psInputBlock, string psInputName)
        {
            Block oBlockIn = dicBlocks[psInputBlock];
            Block oBlockOut = dicBlocks[psOutputBlock];
            Input oInput = oBlockIn.Inputs[psInputName];
            Output oOutput = oBlockOut.Outputs[psOutputName];
            oInput.JoinDefault(oOutput);
        }

        public ControlState CreateControlState(string psName)
        {
            ControlState oControlState = new ControlState(this, psName);
            dicControlStates.Add(psName, oControlState);
            return oControlState;
        }


        public Dictionary<string, ControlState> ControlStates
        {
            get { return dicControlStates; }
        }

        public Dictionary<string, Block> Blocks
        {
            get { return dicBlocks; }
        }

        internal override void GenerateStructure()
        {            
            foreach (ControlState oControlState in ControlStates.Values)
            {
                oControlState.GenerateStructure(ParentComponent);
            }

            foreach (StatusOutput oStatusOutput in dicNewStatusOutputs.Keys)
            {
                NodeVector nvStatusBit = new NodeVector(ParentComponent,oStatusOutput.Name + "_O",1);
                oStatusOutput.LinkNodeVector(nvStatusBit);
            }

            UpdateContainerStatusOutputs();

            foreach (Block oBlock in Blocks.Values)
            {
                oBlock.BaseGenerateStructure(ParentComponent, Clock, Reset);

                foreach (ControlInput oControlInput in oBlock.ControlInputs.Values)
                {
                    if (oControlInput.NodeVector == null)
                    {
                        string sControlInputName = oControlInput.FullName + "_CI";                        
                        oControlInput.LinkNodeVector(ParentComponent.CreateNodeVector(sControlInputName, oControlInput.Width));
                    }
                }
            }
            foreach (StatusOutput oStatusOutput in dicNewStatusOutputs.Keys)
            {
                Output oOutput = dicNewStatusOutputs[oStatusOutput];
                oOutput.NodeVector.Join(oStatusOutput.NodeVector);
            }
            foreach (Block oBlock in Blocks.Values)
            {
                oBlock.ExpandJoins();
            }
        }

        internal void UpdateContainerStatusOutputs()
        {
            foreach (ContainerBlock oContainerBlock in dicContainerBlocks.Values)
            {
                oContainerBlock.UpdateContainerStatusOutputs();
            }
            foreach (Block oBlock in dicBlocks.Values)
            {
                foreach (StatusOutput oStatus in oBlock.StatusOutputs.Values)
                {
                    string sStatusName = oBlock.Name + "_" + oStatus.Name;
                    if (!dicStatusOutputs.ContainsKey(sStatusName))
                        dicStatusOutputs.Add(sStatusName, oStatus);
                }
            }
        }

        public override void AddSignalViews()
        {
            SystemInterface oSystemInterface = ParentComponent.RapidHardware.SystemInterface;
            
            foreach (Block oBlock in Blocks.Values)
            {
                oBlock.AddSignalViews();
            }
            base.AddSignalViews();

            foreach (StatusOutput oStatusOutput in StatusOutputs.Values)
            {
                oSystemInterface.AddSignalView(oStatusOutput.ToString(), oStatusOutput.NodeVector);    
            }
        }

        public Dictionary<string, ExternalIOBlock> ExternalInputs
        {
            get { return dicExternalInputs; }
        }

        public Dictionary<string, ExternalIOBlock> ExternalOutputs
        {
            get { return dicExternalOutputs; }
        }

        public Dictionary<string, ExternalBusBlock> ExternalBusInputs
        {
            get { return dicExternalBusInputs; }
        }

        public Dictionary<string, ExternalBusBlock> ExternalBusOutputs
        {
            get { return dicExternalBusOutputs; }
        }

        public void CreateStatusOutput(string psStatusName, string psBlockName, string psOutputName)
        {
            StatusOutput oStatusOutput = CreateStatusOutput(psStatusName);
            Block oBlock = Blocks[psBlockName];
            Output oOutput = oBlock.Outputs[psOutputName];
            dicNewStatusOutputs.Add(oStatusOutput, oOutput);
        }

        public SignalView AddSignalView(string psBlock, string psName)
        {
            return AddSignalView(psBlock, psName, SignalFormat.Binary);
        }

        public SignalView AddSignalView(string psBlock, string psName, SignalFormat peFormat)
        {
            Block oBlock = Blocks[psBlock];
            string sSignalName = oBlock.AddSignalView(psName);
            dicSignalViews.Add(sSignalName,peFormat);

            SignalView oSignalView = ParentComponent.RapidHardware.SystemInterface.GetSignalView(sSignalName);
            oSignalView.SignalFormat = peFormat;
            return oSignalView;
        }

        /*public void AddStatusView(string psName)
        {

        }*/

        public void SignalDisplay(HardwareScript poScript, int piClockTime)
        {
            poScript.Echo(piClockTime, "----------------------------------------------- " + piClockTime.ToString());            
            poScript.Echo(piClockTime, Name + " Signals");
            foreach (string sName in dicSignalViews.Keys)
            {
                SignalView oSignalView = ParentComponent.RapidHardware.SystemInterface.GetSignalView(sName);
                if (!oSignalView.Hidden)
                    poScript.SignalView(piClockTime, sName,dicSignalViews[sName]);
            }

            poScript.Echo(piClockTime, "-----------------------------------------------" + piClockTime.ToString());            
        }

    }
}
